#pragma once

#include <string>

#include "Common.h"
#include "FileList.h"

/*----------------------------------------------------
 *| 0  |  1  |  2  |  3  |  4 ...-...4+1023 | 1028/9 |
 *+----+-----+-----+-----+------------------+--------+
 *| AA | SEQ | Msg |  R  |    DATA(1024)    |   CRC  |
 *------------------------------------------+--------+*/

#define MSG_HEAD_LEN    4
#define MSG_BODY_LEN    1024
#define MSG_CEC_LEN     2
#define MSG_DATA_LEN    (MSG_HEAD_LEN + MSG_BODY_LEN)
#define MSG_TOTAL_LEN   (MSG_DATA_LEN + MSG_CEC_LEN)

typedef enum
{
    MSG_ACK= 1,              //common ACK
    MSG_NACK,                //common NACK
    MSG_HELLO,               //request : connect to device
    MSG_COMPLETE,            //request : file manager completed

    MSG_FDISK = 8,          //request : get disk info
    MSG_MKFS,               //request : format disk
    MSG_CREATE_FOLDER,      //request : create folder
    MSG_CREATE_FILE,        //request : create file
    MSG_GET_FILEINFO,       //request : get file info
    MSG_OPEN_FILE,          //request : open file 
    MSG_CLOSE_FILE,         //request : close file
    MSG_WRITE_BLOCK,        //request : write block
    MSG_WRITE_REMNANT,      //request : write remnant
    MSG_FILEINFO_RSP,       //response: file info
    MSG_DELETE,             //request : delete file
    MSG_READ_LIST,          //request : read file list of disk
}PktMsg_E;

typedef enum
{
    ST_SETUP,       //setup
    ST_FDISK,       //get disk info
    ST_READLIST,    //read file list
    ST_MKFS,        //format disk
    ST_OPEN,        //open file
    ST_COMPLETE,    //complete
    ST_TRANSMIT,    //transmit
    ST_FAILED,      //failed
    ST_DELETE,      //delete file
    ST_CONNECT,     //connect
    ST_DISCONN,     //disconnect

    ST_WAIT = 0x40, //wait
    ST_WAIT_FILES,  //wait file list response
}PktState_E;

typedef enum
{
    OPT_NORMAL,
    OPT_CONNECT,
    OPT_READFILES,
    OPT_MKFS,
    OPT_DELETE,
    OPT_DISCONN,
}PktOpt_E;

typedef struct
{
    uint8_t   magic;
    uint8_t   seqnum;
    uint8_t   msgid;
    uint8_t   resv;
    uint8_t   body[MSG_BODY_LEN];
    uint16_t  crc16;
}PktMsg_T;

typedef struct
{
    uint8_t     seqnum;
    uint8_t     recv;
    uint16_t    ackcode;
}PktComAck_T;

typedef struct
{
    uint8_t     seqnum;
    uint8_t     resv;
    uint16_t    ackcode;

    uint8_t     state;
    uint8_t     resv2;
    uint16_t    fcrc16;
    uint32_t    fsize;
}PktFileInfo_T;

typedef struct
{
    uint8_t     seqnum;
    uint8_t     resv;
    uint16_t    ackcode;

    uint32_t    part1_total;
    uint32_t    part1_free;
    uint32_t    part2_total;
    uint32_t    part2_free;
}PktDiskInfo_T;

class ISerial;

class IEngine
{
public:
    virtual ~IEngine() {};
    virtual void OnDiskInfo(int iDiskId, uint32_t uTotal, uint32_t uFreex) = 0;
    virtual void OnInsertFile(const char* strPath, bool bIsDir) = 0;
};

class Packet
{
public:
    Packet(ISerial* serial, int step);
    Packet(ISerial* serial, const char* srcpath, const char* dstpath, int type = 0);
    virtual ~Packet();

    PktState_E GetState() { return m_nState; };
    void SetState(PktState_E iState);
    void SetSrcPtah(const char* srcpath);
    void SetDstPath(const char* dstpath);
    int  Process();
    void SetEngine(IEngine* engine) { m_pEngine = engine; };
    void SetLink(ISerial *serial) { m_hLink = serial; };
    int  TestProc();

private:
    uint8_t GetSeqNum();
    int     GetResponse() { return 1; };
    int     SendMsg(PktMsg_T& msg, int retry = 0);
    int     SendMsg(uint8_t* buf,uint32_t size, int retry = 0);
    int     SendRetry();
    int     RecvMsg();
    int     RecvMsgHandler(PktMsg_T& msg);

private:
    IEngine*    m_pEngine;
    ISerial*    m_hLink;
    int         m_nType;
    uint8_t     m_pRSendBuf[UART_DMA_SIZE];
    uint32_t    m_uRSendLen;
    uint64_t    m_uLastTime;
    uint64_t    m_uLastInfoTime;
    uint32_t    m_uReTry;
    bool        m_bWaitFlag;
    uint8_t*    m_ReadBuf;

    char        m_strSrcPath[128];
    char        m_strDstPath[128];
    uint32_t    m_nFileSize;
    uint16_t    m_uFileCRC;
    FILE*       m_hFile;

    uint16_t    m_uTargetCRC;
    uint32_t    m_uTargetSize;

    PktState_E  m_nState;
    PktState_E  m_nNextState;
};

class MCUDev
{
public:
    MCUDev(const char* root);
    ~MCUDev();

public:
    PktMsg_E GetState();

    void Process();
    void bind(ISerial* serial) { m_hLink = serial; };

private:
    uint8_t GetSeqNum();
    int     SendMsg(PktMsg_T& msg);
    int     RecvMsg(PktMsg_T& msg);

private:
    char            m_strRoot[128];
    ISerial*        m_hLink;
    FILE*           m_hFile;
    uint8_t         m_uSeqNum;
    uint16_t        m_uLatWrSeq;
    int             m_nType;

    uint8_t*        m_ReadBuf;
};